package com.avengers.core.kit;

import com.avengers.core.bean.enums.Charset;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author yu.wang
 * @since 2018-04-11 下午8:43
 */
public class EncryptKit {
    /**
     * 加密算法
     */
    public static class Algorithm {
        /**
         * 哈希算法（不可逆，用于校验/签名）：MD5（128位，安全性弱）、SHA-1（160位，逐步淘汰）、SHA-256/SHA-512（推荐，高安全）、HMAC（带密钥的哈希，防篡改）。
         */
        public static final String MD5 = "MD5";
        public static final String HMAC = "HMAC";
        public static final String SHA1 = "SHA-1";
        public static final String SHA256 = "SHA-256";
        public static final String SHA512 = "SHA-512";
        public static final String HMAC_SHA_1 = "HmacSHA1";
        public static final String HMAC_SHA_256 = "HmacSHA256";
        /**
         * 国密算法（合规场景）：SM4（对称）、SM2（非对称）、SM3（哈希），需借助 BouncyCastle 库实现。
         */
        public static final String SM2 = "SM2";
        public static final String SM3 = "SM3";
        public static final String SM4 = "SM4";
        /**
         * 非对称加密（安全，适用于密钥交换/签名）：RSA（最常用）、DSA（仅签名）、ECDSA（椭圆曲线，高效）。
         */
        public static final String RSA = "RSA";
        public static final String DSA = "DSA";
        public static final String ECDSA = "ECDSA";
        /**
         * 对称加密（速度快，适用于大数据）：AES（默认推荐，替代 DES）、DES（过时）、3DES、Blowfish、RC4（不安全，少用）。
         */
        public static final String AES = "AES";
        public static final String DES = "DES";
        public static final String DES3 = "3DES";
        public static final String BLOWFISH = "Blowfish";
        public static final String RC4 = "RC4";

        private Algorithm() {
        }
    }

    private static final Pattern UNICODE_PATTERN = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");

    public static String urlEncode(String value) {
        return urlEncode(value, Charset.UTF8);
    }

    public static String urlDecode(String value) {
        return urlDecode(value, Charset.UTF8);
    }

    public static String urlEncode(String value, Charset charset) {
        try {
            return URLEncoder.encode(value, charset.value);
        } catch (UnsupportedEncodingException e) {
            return value;
        }
    }

    public static String urlDecode(String value, Charset charset) {
        try {
            return URLDecoder.decode(value, charset.value);
        } catch (UnsupportedEncodingException e) {
            return value;
        }
    }

    /**
     * 将字符串中的Unicode中文编码转回中文
     */
    public static String decodeUnicode(String str) {
        Matcher matcher = UNICODE_PATTERN.matcher(str);
        char ch;
        while (matcher.find()) {
            String group = matcher.group(2);
            ch = (char) Integer.parseInt(group, 16);
            String group1 = matcher.group(1);
            str = str.replace(group1, ch + "");
        }
        return str;
    }

    /**
     * MD5 加密
     *
     * @param string 字符串
     * @return 加密后字符串
     */
    public static String md5(String string) {
        return md5(string, Charset.UTF8.value);
    }

    /**
     * MD5 加密
     */
    public static String md5(String string, String charset) {
        return DigestUtils.md5Hex(getContentBytes(string, charset));
    }


    /**
     * 参数签名
     */
    public static String sign(Map<String, String> paramMap, String key) {
        return sign(paramMap, key, false);
    }

    public static String sign(Map<String, String> paramMap, String key, boolean encode) {
        return sign(paramMap, key, Charset.UTF8.value, encode);
    }

    /**
     * 参数签名
     */
    public static String sign(Map<String, String> paramMap, String key, String charset) {
        return sign(paramMap, key, charset, false);
    }

    public static String sign(Map<String, String> paramMap, String key, String charset, boolean encode) {
        String text = StringKit.mapToHttpParams(paramMap, encode);
        return sign(text, key, charset);
    }

    /**
     * 签名字符串
     *
     * @param text 需要签名的字符串
     * @param key  密钥
     * @return 签名结果
     */
    public static String sign(String text, String key) {
        return sign(text, key, Charset.UTF8.value);
    }

    /**
     * 签名字符串
     *
     * @param text    需要签名的字符串
     * @param key     密钥
     * @param charset 编码格式
     * @return 签名结果
     */
    public static String sign(String text, String key, String charset) {
        return DigestUtils.md5Hex(getContentBytes(text + key, charset));
    }

    /**
     * 校验签名是否正确
     *
     * @param paramMap 需要签名的参数map
     * @param sign     签名结果
     * @param key      密钥
     * @return 签名是否正确
     */
    public static boolean verifyHttpParam(Map<String, String[]> paramMap, String sign, String key) {
        return verifyHttpParam(paramMap, sign, key, false);
    }

    public static boolean verifyHttpParam(Map<String, String[]> paramMap, String sign, String key, boolean encode) {
        return verifyHttpParam(paramMap, sign, key, Charset.UTF8.value, encode);
    }

    /**
     * 校验签名是否正确
     *
     * @param paramMap 需要签名的参数map
     * @param sign     签名结果
     * @param key      密钥
     * @param charset  编码格式
     * @return 签名是否正确
     */
    public static boolean verifyHttpParam(Map<String, String[]> paramMap, String sign, String key, String charset) {
        return verifyHttpParam(paramMap, sign, key, charset, false);
    }

    public static boolean verifyHttpParam(Map<String, String[]> paramMap, String sign, String key, String charset, boolean encode) {
        String paramStr = StringKit.mapToHttpArrayParams(paramMap, encode);
        return verify(paramStr, sign, key, charset);
    }

    /**
     * 校验签名是否正确
     *
     * @param paramMap 需要签名的参数map
     * @param sign     签名结果
     * @param key      密钥
     * @return 签名是否正确
     */
    public static boolean verify(Map<String, String> paramMap, String sign, String key) {
        return verify(paramMap, sign, key, false);
    }

    public static boolean verify(Map<String, String> paramMap, String sign, String key, boolean encode) {
        return verify(paramMap, sign, key, Charset.UTF8.value, encode);
    }

    /**
     * 校验签名是否正确
     *
     * @param paramMap 需要签名的参数map
     * @param sign     签名结果
     * @param key      密钥
     * @param charset  编码格式
     * @return 签名是否正确
     */
    public static boolean verify(Map<String, String> paramMap, String sign, String key, String charset) {
        return verify(paramMap, sign, key, charset, false);
    }

    public static boolean verify(Map<String, String> paramMap, String sign, String key, String charset, boolean encode) {
        String mySign = sign(paramMap, key, charset, encode);

        return mySign.equals(sign);
    }

    /**
     * 签名字符串
     *
     * @param text 需要签名的字符串
     * @param sign 签名结果
     * @param key  密钥
     * @return 签名结果
     */
    public static boolean verify(String text, String sign, String key) {
        return verify(text, sign, key, Charset.UTF8.value);
    }

    /**
     * 签名字符串
     *
     * @param text    需要签名的字符串
     * @param sign    签名结果
     * @param key     密钥
     * @param charset 编码格式
     * @return 签名结果
     */
    public static boolean verify(String text, String sign, String key, String charset) {
        String mySign = sign(text, key, charset);
        return mySign.equals(sign);
    }

    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || charset.isEmpty()) {
            return null;
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }
}
